package com.joe.demo.nio.io;

import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;

/**
 * @Description
 * @Author gaojw
 * @Date 2024-08-28
 */
@Slf4j
public class WriteServer {

    public static void main(String[] args) throws IOException {
        final ServerSocketChannel server = ServerSocketChannel.open();
        server.configureBlocking(false);

        final Selector selector = Selector.open();
        server.register(selector, SelectionKey.OP_ACCEPT);

        server.bind(new InetSocketAddress(8083));

        while (true){
            selector.select();

            final Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()){
                final SelectionKey key = iterator.next();
                iterator.remove();
                if (key.isAcceptable()){
                    final SocketChannel socketChannel = server.accept();
                    socketChannel.configureBlocking(false);
                    final SelectionKey sockKey = socketChannel.register(selector, SelectionKey.OP_READ, null);
                    // 1. 向客户端发送大量数据
                    final StringBuilder stringBuilder = new StringBuilder();
                    for (int i = 0; i < 300000; i++) {
                        stringBuilder.append("a");
                    }
                    final ByteBuffer buffer = StandardCharsets.UTF_8.encode(stringBuilder.toString());
                    // 2. 返回值，代表实际写入的字节数
                    final int write = socketChannel.write(buffer);
                    log.info("已经发送：{}", write);
                    // 3. 判断是否有剩余内容
                    if (buffer.hasRemaining()){
                        // 4. 关注可写事件
                        sockKey.interestOps(sockKey.interestOps() + SelectionKey.OP_WRITE);
                        // 5. 把未写完的内容挂在 SelectionKey
                        sockKey.attach(buffer);
                    }
                } else if (key.isReadable()){
                    try {
                        final SocketChannel socketChannel = (SocketChannel) key.channel();
                        // 获取 selectionKey 上关联的附件
                        final ByteBuffer buffer = (ByteBuffer) key.attachment();
                        // 正常断开 read -1
                        final int read = socketChannel.read(buffer);
                        if (read == -1){
                            key.cancel();
                        } else {
                            // buffer.flip();
                            // log.info("接收到的数据: {}", StandardCharsets.UTF_8.decode(buffer));
                            split(buffer);
                            if (buffer.position() == buffer.limit()){
                                final ByteBuffer newBuffer = ByteBuffer.allocate(buffer.capacity() * 2);
                                buffer.flip();
                                newBuffer.put(buffer);
                                key.attach(newBuffer);
                            }
                        }
                    } catch (IOException ex){
                        ex.printStackTrace();
                        // 客户端断开，需要将 key 取消，就是 selectedKeys 中删除
                        key.cancel();
                    }
                } else if (key.isWritable()){
                    final ByteBuffer buffer = (ByteBuffer) key.attachment();
                    final SocketChannel socketChannel = (SocketChannel) key.channel();
                    final int write = socketChannel.write(buffer);
                    log.info("可写事件，已经发送：{}", write);
                    if (!buffer.hasRemaining()){
                        // 需要清除 buffer
                        key.attach(null);
                        // 不需要关注可写事件
                        key.interestOps(key.interestOps() - SelectionKey.OP_WRITE);
                    }
                } else {
                    key.cancel();
                }
            }
        }

    }

    public static void split(ByteBuffer buffer){
        buffer.flip();
        for (int i = 0; i < buffer.limit(); i++) {
            // 找到一条完整消息
            if (buffer.get(i) == '\n') {
                //存入新的 ByteBuffer
                int length = i + 1 - buffer.position();
                ByteBuffer target = ByteBuffer.allocate(length);
                for (int j = 0; j < length; j++) {
                    byte b = buffer.get();
                    // log.info("字符：{}", (char) b);
                    target.put(b);
                }
                target.flip();
                log.info("消息: {}", StandardCharsets.UTF_8.decode(target));
            }
        }
        buffer.compact();
    }

}
